package com.katze.boot.plugins.shiro.aspect;

import org.apache.shiro.SecurityUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.core.NamedThreadLocal;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.Objects;

/**
 * AOP 记录用户操作日志
 *
 * @author MrBird
 * @link https://mrbird.cc/Spring-Boot-AOP%20log.html
 */
@Aspect
@Component
@ConditionalOnBean(LogAspectService.class)
public class LogAspect {
    private static final ThreadLocal<Date> localThread = new NamedThreadLocal<>("BeginTime_ThreadLocal");

    @Resource
    private LogAspectService logAspectService;



    @Pointcut("@annotation(com.katze.boot.plugins.shiro.annotations.RestRequestMapping)")
    public void pointcut() {
        // do nothing
    }

    /**
     * 前置通知 (在方法执行之前返回)用于拦截Controller层记录用户的操作的开始时间
     *
     * @param joinPoint 切点
     */
    @Before("pointcut()")
    public void doBefore(JoinPoint joinPoint) {
        //线程绑定变量（该数据只有当前请求的线程可见）
        localThread.set(new Date());
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        // 执行方法
        Object result = point.proceed();
        // 执行时长(毫秒)
        long duration = System.currentTimeMillis() - localThread.get().getTime();
        // 获取 request
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        // 获取Shiro框架下用户信息
        Object principal = SecurityUtils.getSubject().getPrincipal();
        // 保存日志
        logAspectService.around(point, principal, duration, request);
        return result;
    }
}
